﻿using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Sgr.BackGroundTasks;
using Sgr.Domain.Uow;
using Sgr.EventBus.RelayBoxs.Domain;
using Sgr.Utilities;
using System.Collections.Concurrent;

namespace Sgr.EventBus.RelayBoxs.BackGroundTasks
{
    public class OutboxMessageProcessorBackGroundTask : IBackGroundTask
    {
        //private static readonly ConcurrentDictionary<string, Type> _typeCache = new();

        private readonly ILogger<OutboxMessageProcessorBackGroundTask> _logger;
        private readonly IOutboxMessageRepository _outboxMessageRepository;
        private readonly IUnitOfWorkManager _unitOfWorkManager;
        private readonly RelayBoxOptions _relayBoxOptions;
        private readonly IServiceProvider _serviceProvider;

        public OutboxMessageProcessorBackGroundTask(
            IServiceProvider serviceProvider,
            IOutboxMessageRepository outboxMessageRepository,
            IUnitOfWorkManager unitOfWorkManager,
            IOptions<RelayBoxOptions> options,
            ILogger<OutboxMessageProcessorBackGroundTask> logger)
        {
            _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
            _outboxMessageRepository = outboxMessageRepository ?? throw new ArgumentNullException(nameof(outboxMessageRepository));
            _unitOfWorkManager = unitOfWorkManager ?? throw new ArgumentNullException(nameof(unitOfWorkManager));
            _relayBoxOptions = options?.Value ?? throw new ArgumentNullException(nameof(options));
            _logger = logger ?? throw new ArgumentNullException(nameof(logger));
        }

        public async Task ExecuteAsync(CancellationToken cancellationToken = default)
        {
            _logger.LogDebug($"OutboxMessageProcessorBackGroundTask 于 {DateTime.UtcNow} 开始处理 Outbox 消息");

            try
            {
                // 后续优化思考
                // 1. 维护一个SortSet内存队列，维护Outbox表的同时读写内存队列，借助内存队列来判断是否有待处理消息，减少数据库扫描
                // 2. 根据每次扫描结果动态调整扫描间隔时间，减少空轮询

                OutboxMessage[] messages = await GetPendingOutboxMessagesAsync(cancellationToken);

                if (messages.Length > 0)
                {
                    List<long> dispatchedIds = new List<long>();
                    List<OutboxMessage> errors = new List<OutboxMessage>();

                    foreach (var message in messages)
                    {
                        if (message == null)
                            continue;

                        if (message.RetryCount >= _relayBoxOptions.MaxRetryAttempts)
                        {
                            errors.Add(message);                        // 超过最大重试次数，加入错误列表
                            continue;
                        }

                        if (!await ProcessSingleMessageAsync(message, cancellationToken))
                            errors.Add(message);                    // 处理失败，加入错误列表
                        else
                            dispatchedIds.Add(message.Id);             // 处理成功，加入已发送列表
                    }

                    // 批量更新状态
                    if (dispatchedIds.Count > 0)
                        await ProcessDispatchedMessageAsync(dispatchedIds, cancellationToken);

                    if (errors.Count > 0)
                        await ProcessErrorMessageAsync(errors, cancellationToken);
                }

                _logger.LogDebug($"OutboxMessageProcessorBackGroundTask 处理 Outbox 消息任务完成。");
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "OutboxMessageProcessorBackGroundTask 处理 Outbox 消息任务执行失败");
                throw;
            }
        }

        private async Task<OutboxMessage[]> GetPendingOutboxMessagesAsync(CancellationToken cancellationToken = default)
        {
            using var uow = _unitOfWorkManager.Begin(isTransactional: false);
            return (await _outboxMessageRepository.GetPendingMessagesAsync(_relayBoxOptions.ScanBatchSize, cancellationToken)).ToArray();
        }

        protected virtual async Task<bool> ProcessSingleMessageAsync(OutboxMessage message, CancellationToken cancellationToken = default)
        {
            try
            {
                if (string.IsNullOrEmpty(message.MessageType))
                {
                    message.LastError = "消息类型为空";
                    return false;
                }

                if (string.IsNullOrWhiteSpace(message.Payload))
                {
                    message.LastError = "消息内容为空";
                    return false;
                }

                //if (!_typeCache.TryGetValue(message.MessageType, out var eventType))
                //{
                //    var type = Type.GetType(message.MessageType, throwOnError: false);
                //    if (type == null)
                //    {
                //        message.LastError = $"无法解析事件类型: {message.MessageType}";
                //        return false;
                //    }
                //    eventType = _typeCache.GetOrAdd(message.MessageType, _ => type);
                //}

                Type? eventType = IntegrationEventTypes.ResolveType(message.MessageType);
                if (eventType == null)
                {
                    message.LastError = $"无法解析事件类型: {message.MessageType}";
                    return false;
                }

                // 获取对应的事件处理器,集成事件使用AddIntegrationEventHandler方法完成注册
                var integrationEventHandlers = _serviceProvider.GetKeyedServices<IIntegrationEventHandler>(message.MessageType).ToList();
                if (integrationEventHandlers.Count == 0)
                {
                    message.LastError = $"未找到 {message.MessageType} 事件的处理器";
                    return false;
                }

                var @event = (IntegrationEvent)JsonHelper.DeserializeObject(eventType, message.Payload)!;
                if (@event == null)
                {
                    message.LastError = "消息内容反序列化失败";
                    return false;
                }

                // 策略说明
                // 1.每个处理器独立事务
                // 2.所有处理器成功，消息才标记为已发送
                // 3.任一处理器失败，消息标记为失败，等待下次重试
                // 4.处理器内部异常由各自负责捕获和记录日志
                // 5.本方法捕获异常，记录到消息的 LastError 字段
                // 6.当前实现不考虑消息投递顺序
                // 7.幂等性由各个处理器负责

                List<string> errHandlers = new List<string>();

                foreach (var handler in integrationEventHandlers)
                {
                    try
                    {
                        await handler.HandleAsync(@event, cancellationToken);
                    }
                    catch (Exception ex)
                    {
                        // 后续完善可以捕获UnrecoverableIntegrationEventException异常，标记为不可恢复错误，不再重试

                        var name = handler.GetType().FullName ?? "Unknown";
                        errHandlers.Add($"{name} : {ex.Message}");
                        _logger.LogError(ex, "处理器执行失败。消息ID: {MessageId}, 类型: {MessageType}, 处理器: {Handler}", message.Id, message.MessageType, name);
                    }
                }

                if (errHandlers.Count > 0)
                {
                    message.LastError = string.Join(';', errHandlers);
                    return false;
                }

                return true;
            }
            catch (Exception ex)
            {
                message.LastError = ex.Message;
                _logger.LogError(ex, "处理消息时发生未预期异常。消息ID: {MessageId}, 类型: {MessageType}", message.Id, message?.MessageType);
                return false;
            }
        }

        protected virtual async Task ProcessDispatchedMessageAsync(List<long> dispatchedIds, CancellationToken cancellationToken = default)
        {
            using (var uow = _unitOfWorkManager.Begin())
            {
                try
                {
                    await _outboxMessageRepository.MarkDispatchedAsync(dispatchedIds, cancellationToken);
                    await uow.CommitAsync(cancellationToken);

                    _logger.LogInformation("OutboxMessageProcessorBackGroundTask 更新执行成功的消息状态，共计 {0} 条", dispatchedIds.Count);
                }
                catch (Exception ex)
                {
                    await uow.RollbackAsync(cancellationToken);
                    _logger.LogError(ex, "OutboxMessageProcessorBackGroundTask 更新执行成功的消息状态失败");
                }
            }
        }

        protected virtual async Task ProcessErrorMessageAsync(List<OutboxMessage> errorMessages, CancellationToken cancellationToken = default)
        {
            using (var uow = _unitOfWorkManager.Begin())
            {
                try
                {
                    foreach (var message in errorMessages)
                    {
                        if (message == null)
                            continue;

                        if (message.RetryCount >= _relayBoxOptions.MaxRetryAttempts)
                        {
                            await _outboxMessageRepository.MarkFailedAsync(message.Id, message.LastError ?? "", cancellationToken);
                        }
                        else
                        {
                            // 计算下一次重试时间
                            int nextDelaySeconds = (int)(Math.Pow(2, message.RetryCount) * _relayBoxOptions.RetryIntervalBase);
                            if (nextDelaySeconds > _relayBoxOptions.MaxRetryInterval)
                                nextDelaySeconds = _relayBoxOptions.MaxRetryInterval;

                            var nextOnUtc = DateTime.UtcNow.AddSeconds(nextDelaySeconds);
                            await _outboxMessageRepository.MarkRetryAsync(message.Id, nextOnUtc, message.LastError ?? "", cancellationToken);
                        }
                    }

                    await uow.CommitAsync(cancellationToken);

                    _logger.LogInformation("OutboxMessageProcessorBackGroundTask 更新执行失败的消息状态，共计 {0} 条", errorMessages.Count);
                }
                catch (Exception ex)
                {
                    await uow.RollbackAsync(cancellationToken);
                    _logger.LogError(ex, "OutboxMessageProcessorBackGroundTask 更新执行失败的消息状态失败");
                }
            }
        }
    }
}